#ifndef __SS_H__
#define __SS_H__

#include"util.hpp"
#include<unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
typedef enum {UNLOGGIN,LOGGIN} ss_status;
class session{
    private:
        uint64_t _ssid;
        //用户名
        uint64_t _uid;
        ss_status _stat;
        wsserver_t::timer_ptr _tp;
    public:
        session(uint64_t ssid):_ssid(ssid)
        {
            DLOG("session:%p,正在创建",this);
        }
        ~session()
        {
            DLOG("session:%p,正在销毁",this);
        }
        //session类中的元素只用设定好就行了，之后调用的时候也只是来判断和查看这些元素是什么。
        //部分用于session_manager中用于管理session
        uint64_t get_ssid(){return _ssid;}
        void set_user(uint64_t uid){_uid = uid;}
        uint64_t get_user(){return _uid;}
        void set_status(ss_status sst){_stat = sst;}
        bool is_loggin(){return _stat == LOGGIN;}
        
        void set_timer(const wsserver_t::timer_ptr &tp){_tp = tp;}
        wsserver_t::timer_ptr& get_timer(){return _tp;}
};
using ss_ptr = std::shared_ptr<session>;
#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
class session_manager{
    private:
        //对不同的session的管理：
        std::mutex _mutex;
        std::unordered_map<uint64_t,ss_ptr> _sessions;
        uint64_t _next_sid;
        wsserver_t *_wsr;
    public:
        session_manager(wsserver_t *wsr):_next_sid(1),_wsr(wsr)
        {
            DLOG("session管理类:%p,创建成功",this);
        }
        ~session_manager(){DLOG("session管理类:%p,销毁中",this);}
        //创建session并进行管理
        ss_ptr session_create(uint64_t uid,ss_status st)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            ss_ptr ssp(new session(_next_sid));
            ssp->set_status(st);
            ssp->set_user(uid);
            _sessions.insert({_next_sid,ssp});
            _next_sid++;
            return ssp;
        }
        //因为timer_ptr的原因所以要对当前的session重新自我添加
        void self_append_session(const ss_ptr& sp)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _sessions.insert({sp->get_ssid(),sp});
            return;
        }
        //销毁session
        void session_destroy(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _sessions.erase(ssid);
        }
        //获取session
        ss_ptr get_session_by_sid(uint64_t ssid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _sessions.find(ssid);
            if(it == _sessions.end())
            {
                return ss_ptr();
            }
            return it->second;
        }
        //session超时管理
        //只能指针的调用要用get接口
        void set_session_expire_time(uint64_t ssid,int ms)
        {
            ss_ptr ssp = get_session_by_sid(ssid);
            if(ssp.get() == nullptr) return;

            //找到了,接下来就是要设置时间函数了
            //下面的设置timer的作用在这里体现。
            wsserver_t::timer_ptr tp = ssp->get_timer();
            //游戏里必须是长连接（不能因为用户在思考期间直接断线，除非有这个需求（挂机）），但是在游戏外只要用户没操作应该将其关闭。
            
            //4种情况：
            //1.此时用户在游戏中，应该为永久状态,说明没有设置时间任务
            if(tp.get() == nullptr && ms == SESSION_FOREVER)
            {
                return;
            }
            //2.此时用户在游戏中但是即将退出游戏，应该将永久状态改为限时,说明此时也没时间任务，但是要进行设置时间任务了
            else if(tp.get() == nullptr && ms != SESSION_FOREVER)
            {              
                wsserver_t::timer_ptr tmp_tp = _wsr->set_timer(ms,std::bind(&session_manager::session_destroy,this,ssp->get_ssid()));
                ssp->set_timer(tmp_tp);
            }
            //3.此时用户在大厅中，应该为限时状态 说明此时有限时任务，此时用户又发送请求了，那这个限时任务要进行重置
            else if(tp.get() != nullptr && ms != SESSION_FOREVER)
            {
                //cancel后任务会被立即执行，所以这时session被删除，
                tp->cancel();
                ssp->set_timer(wsserver_t::timer_ptr());
                //它并不是立即被执行而是要等这个指令被服务器接收并执行，所以可能出现重新设置后再被关闭的情况类似不上锁的情况
                //所以要注意不能直接insert进入hash表，而是采用0ms加一个任务让其执行，然后websocket会根据任务先后执行
                _wsr->set_timer(0,std::bind(&session_manager::self_append_session,this,ssp));//这一步因为是不用被设置进ssp中的因为，这是立即执行的。
                wsserver_t::timer_ptr tmp_tp = _wsr->set_timer(ms,std::bind(&session_manager::session_destroy,this,ssp->get_ssid()));
                ssp->set_timer(tmp_tp);
            }
            //4.用户即将进入游戏，应该将限时改为永久状态,即目前是有限时任务的，但是此时要把限时任务删除。
            else if(tp.get() != nullptr && ms == SESSION_FOREVER)
            {
                //cancel后任务会被立即执行，所以这时session被删除，
                tp->cancel();
                ssp->set_timer(wsserver_t::timer_ptr());
                //它并不是立即被执行而是要等这个指令被服务器接收并执行，所以可能出现重新设置后再被关闭的情况类似不上锁的情况
                //所以要注意不能直接insert进入hash表，而是采用0ms加一个任务让其执行，然后websocket会根据任务先后执行
                _wsr->set_timer(0,std::bind(&session_manager::self_append_session,this,ssp));//这一步因为是不用被设置进ssp中的因为，这是立即执行的。
            }
        }
};

#endif